﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace InnovTech.Data
{
    /// <summary>
    /// this class aims the references of the big objects which should be released when not be used any more
    /// to save the memory. this object manage these big objects with WeakReference so that they can be released 
    /// when there is no reference of them any more. specially for the UI-Elements
    /// </summary>
    /// <typeparam name="T">generic type which should be managed in this list</typeparam>
    public class WeakRefList<T> : List<WeakReference>
    {
        /// <summary>
        /// the limit number of the elements in this list 
        /// to toggle to remove the WeakReferences whose Target has been released
        /// </summary>
        protected int _gcLimit = 1;
        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="gcLimit">the limit number to toggle removing released references</param>
        public WeakRefList(int gcLimit):base()
        {
            _gcLimit = gcLimit;
        }
        /// <summary>
        /// the managed object type in this list
        /// </summary>
        public Type RefTargetType { get { return typeof(T); } }

        /// <summary>
        /// add a object to this list
        /// </summary>
        /// <param name="obj"></param>
        public void Add(T obj)
        {
            if (!Contains(obj))
            {
                Add(new WeakReference(obj));
            }
        }
        /// <summary>
        /// add a set of objects in this list
        /// </summary>
        /// <param name="objs">a set of objets to add</param>
        public void AddRange(IEnumerable<T> objs)
        {
            foreach (T obj in objs)
            {
                Add(obj);
            }
        }
        /// <summary>
        /// remove a object from this list
        /// </summary>
        /// <param name="obj">the object to remove</param>
        public void Remove(T obj)
        {
            RemoveAll(delegate(T t) { return t.Equals(obj); });
        }
        /// <summary>
        /// remove all objects according with the given predicate
        /// </summary>
        /// <param name="predicate">the predicate to decide which objects should be removed</param>
        public void RemoveAll(Predicate<T> predicate)
        {
            RemoveGCRefs();
            RemoveAll(wr => predicate((T)wr.Target));
        }
        /// <summary>
        /// indicate whether a object is contained by this list
        /// </summary>
        /// <param name="obj">a object</param>
        /// <returns>true, when this list contains the given object</returns>
        public bool Contains(T obj)
        {
            RemoveGCRefs();
            return Find(wr => wr.Target.Equals(obj)) != null;
        }
        /// <summary>
        /// get the object which complies the given condition (predicate)
        /// </summary>
        /// <param name="predicate">a condition to select object</param>
        /// <returns>a object which is in accordance with the given condition.
        /// when no one be found, null</returns>
        public T GetRefObject(Predicate<T> predicate)
        {
            //RemoveGCRefs();
            //WeakReference result = Find(wr => predicate((T)wr.Target));
            //return result == null ? default(T) : (T)result.Target;
            IEnumerable<T> results = GetRefObjects(predicate);
            return results.Count() > 0 ? results.First() : default(T);
        }
        /// <summary>
        /// get the object which complies the given condition (predicate)
        /// </summary>
        /// <param name="predicate">a condition to select objects</param>
        /// <returns>a set of objects which are in accordance with the given condition.</returns>
        public IEnumerable<T> GetRefObjects(Predicate<T> predicate)
        {
            RemoveGCRefs();
            return FindAll(wr => predicate((T)wr.Target)).Select<WeakReference, T>(wr => (T)wr.Target);
        }
        /// <summary>
        /// get all referenced objects in this list
        /// </summary>
        public List<T> ReferencedObjects
        {
            get
            {
                RemoveGCRefs();
                return GetRefObjects(obj => true).ToList();
            }
        }

        /// <summary>
        /// indicate whether the target of a WeakReference instance is released
        /// </summary>
        /// <param name="wr">a WeakReference Instance</param>
        /// <returns>true, released</returns>
        protected bool IsReleased(WeakReference wr)
        {
            return !wr.IsAlive || wr.Target == null;
        }
        /// <summary>
        /// remove all WeakReference Instances whose target has been released
        /// </summary>
        protected void RemoveGCRefs()
        {
            if(this.Count >= _gcLimit)
                RemoveAll(IsReleased);
        }

        

        
    }
}
